package bird.stack.array_stack;


/**
 * @author sijunqiang
 * @data 2021/5/18 23:41
 * @description: 自定定义动态数组结构
 */
public class Array<E> {

    //定义一个数组 用来存储元素
    private E[] data;

    // 定义一个属性 代表着数组中实际存储的元素的个数
    private int size;

    //带参的构造函数
    public Array(int capacity) {
        data = (E[]) new Object[capacity];
        size = 0;
    }


    // 空参构造
    public Array() {
        this(10);
    }

    // 1:获取数组中元素的个数
    public int getSize() {
        return size;
    }

    // 2:获取数组的容量
    public int getCapacity() {
        return data.length;
    }

    //3:判断数组是否为空
    public Boolean isEmpty() {
        return size == 0;
    }


    //4:向数组中添加元素
    public void addLast(E e) {
        add(size, e);
    }

    //5:向数组中首位添加元素
    public void addFirst(E e) {
        add(0, e);
    }

    // 6:向数组中指定位置添加元素
    public void add(int index, E e) {

        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add fail array is full！index is illegal!");
        }
        if (size == data.length) {
            resize(2 * data.length);
        }

        //  在指定的位置上面添加元素
        //  思路：从后往前 依次的将前一个的值 赋值给后一个值
        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
        data[index] = e;
        size++;
    }


    // 7：丛数组中取出有元素
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("get fail. Index is illegal!");
        }
        return data[index];
    }


    // 8：修改数组中指定位置上面的元素 返回原来的索引上面的元素
    public E set(int index, E e) {
        E ret = get(index);
        data[index] = e;
        return ret;
    }

    // 9：查看数组中是否包含元素e 存在就返回 true 不存在就返回false
    public Boolean contains(E e) {
        int ret = find(e);
        if (ret != -1) {
            return true;
        }
        return false;
    }

    // 10： 查看元素e在数组中位置 存在就返回这个元素所在的索引的位置 不存在就返回-1;
    public int find(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }

    //11: 删除指定索引上面的元素
    public E remove(int index) {

        //1：先判断索引是否合法
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("remove fail. index is illegal!");
        }

        E ret = get(index);
        //2: 执行删除的逻辑 思路：无论数组中是新增还是删除元素 思路就是先找到这个元素后面的元素然后再往后操作
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size--;

        // 缩容  /4 采用的是懒加载的方式 防止复杂度震荡
        if (size == data.length / 4 && data.length / 2 != 0) {
            resize(data.length / 2);
        }
        return ret;
    }

    // 13：删除数组中第一个索引上面的元素 返回这个索引上面的元素
    public E removeFirst() {
        return remove(0);
    }

    // 14:删除数组中最后一个索引上面的元素,返回这个元素
    public E removeLast() {
        return remove(size - 1);
    }

    //15:删除数组中任何一个元素
    public Boolean removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
            return true;
        }
        return false;
    }

    public E getLast() {
        return get(size - 1);
    }

    public E getFirst() {
        return get(0);
    }

    // 数组动态的扩容 扩容的机制 就是当size==data.length 的长度的时候.
    private void resize(int newCapacity) {
        // 扩容的机制就是,重新new 一个新的数组 然后将老的数组遍历依次的赋值给新的数组
        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }


    // toString-方法  重写父类的方法 目的是使用system.out.print();打印自定义的内容，而不是父类中的地址
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append(String.format("array: capacity= %d, size= %d\n", data.length, size));
        res.append('[');
        // 打印数组中里面的元素,size-代表数组中真正存储的元素
        for (int i = 0; i < size; i++) {
            res.append(data[i]);
            // 如果不是最后一个元素 那么就需要进行拼接 "," 这样的话 就可以不用在截取最后一位了
            if (i != size - 1) {
                res.append(',');
            }
        }
        res.append(']');
        return res.toString();
    }

}
